Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smoother rivers connections on overmaps edges #3178

Merged
merged 3 commits into from
Sep 20, 2023

Conversation

Vollch
Copy link
Contributor

@Vollch Vollch commented Sep 18, 2023

Summary

SUMMARY: Bugfixes "Smoother rivers connections on overmaps edges"

Purpose of change

River connections and side touches are often generated with cut edge next to overmap border, without any shore. This change polish all those edges.

Describe the solution

River polishing function will now be aware of adjacent tiles even if they're on different overmap, so it knows where shores should be placed. For not yet generated overmaps it assumes that water continues as it goes, leaving polishing to their mapgens, which can handle that nicely.

Describe alternatives you've considered

Rewriting overmap::place_river to make starting points of rivers always match each others, and never touch borders in any other places.

Testing

Jumped between overmaps in debug mode, following river. Looks much better now.

Additional context

It's a bit difficult to show the difference due to map randomness, but that should give an idea what it does on pretty similar layouts.
Overmap edge, before fix, and after fix:
image

@github-actions github-actions bot added the src changes related to source code. label Sep 18, 2023
@scarf005 scarf005 requested a review from olanti-p September 18, 2023 22:51
@Zireael07
Copy link
Contributor

Worth porting back to CDDA itself <3

@scarf005
Copy link
Member

@Zireael07 hi, thanks for the interest in our project. however, as this is BN repository and not DDA repository, could you open a feature request on DDA issues instead? you could mention the PR you want to port via leaving a url on DDA issue, such as:

https://github.com/cataclysmbnteam/Cataclysm-BN/pull/3178

and it would link to this PR automatically.

@Zireael07
Copy link
Contributor

I know, however many of BN contributors are also DDA contributors and often will open PRs themselves. (Opening a PR necessitates you test the changes, which means you need the ability to compile from source)

Copy link
Contributor

@olanti-p olanti-p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MacOS build timed out, but that was most likely unrelated.

Comment on lines +4041 to +4047
if( ( x > 0 && y > 0 &&
!is_river_or_lake( ter( { x - 1, y - 1, 0} ) ) ) ||
( x > 0 && y == 0 && north != nullptr &&
!is_river_or_lake( north->ter( { x - 1, OMAPY - 1, 0} ) ) ) ||
( x == 0 && y > 0 && west != nullptr &&
!is_river_or_lake( west->ter( { OMAPX - 1, y - 1, 0} ) ) )
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an easy way to extract these checks into some function, or closure, that would take x and y and decide for which of the 5 overmaps, and at what point, to call the ter method?

continue;
}

// For ungenerated overmaps we're assuming that terra incognita is covered by water, and leaving polishing to its mapgen
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything from this line and to the end of the loop should really be extracted into a separate function.
good_river was a terrible name, but something like polish_river_tile(x,y,north,south,east,west) simply begs to exist here.
Usually the less nested ifs and fors there are in a function, the more readable it is.

src/overmap.h Outdated Show resolved Hide resolved
@Vollch
Copy link
Contributor Author

Vollch commented Sep 19, 2023

Is there an easy way to extract these checks into some function, or closure, that would take x and y and decide for which of the 5 overmaps, and at what point, to call the ter method?

om_dir get_dir(point p) { //Enum declaration skipped
    if (p.x < 0) {
        if (p.y < 0) {
            return DIR_NORTHWEST;
        } else if (p.y > OMAPY - 1) {
            return DIR_SOUTHWEST;
        } else {
            return DIR_WEST;
        }
    } else if (p.x > OMAPX - 1) {
        if (p.y < 0) {
            return DIR_NORTHEAST;
        } else if (p.y > OMAPY - 1) {
            return DIR_SOUTHEAST;
        } else {
            return DIR_EAST;
        }
    } else if (p.y < 0) {
        return DIR_NORTH;
    } else if (p.y > OMAPY - 1) {
        return DIR_SOUTH;
    } else {
        return DIR_HERE;
    }
}

void shift_pos(point p) {
    if (p.x < 0) {
        p.x = OMAPX - 1 + p.x;
    } else if (p.x > OMAPX - 1) {
        p.x = p.x - OMAPX - 1;
    } 
    if (p.y < 0) {
        p.y = OMAPY - 1 + p.y;
    } else if (p.y > OMAPY - 1) {
        p.y = p.y - OMAPY - 1;
    }
}

bool is_corner_needed(overmap o, point p) {
    return o !== nullptr && !is_river_or_lake( o->ter( p ) );
}

...
overmap* om[DIR_MAX] = { nullptr };
om[DIR_NORTHEAST] = nullptr; // Diagonal overmaps lookup *could* be useful, if they'd be already loaded. Since they're not - it doesn't worth the hassle.
om[DIR_NORTH] = north;
om[DIR_HERE] = this;
...
if ( is_corner_needed(om[get_dir(p + point_north_east)], shift_pos(p + point_north_east)) ) {
    ter_set( p, oter_id( "river_c_not_ne" ) );
}

Everything can be refactored. Like to this monstrosity. But it doesn't seems much better to me - two screens of code scattered around where few lines of checks could do the work. Those checks looks ugly, i admit that, but they're trivial and basically same. After spending 5 seconds looking at first one you can tell immediately what all the other ones does. And it's write once code anyway, not something that will need to be tweaked further. (Unless i screwed offsets somewhere ofc, but it won't take a long to notice and fix)
Plus, above solution have worse performance, due to more generic checks, needless functions calls, and other boilerplates. I'd be happy to refactor it to something clean and costless, but i don't see how can it be done - above solution will add 8 extra calls per most cells, having still debatable readability due to needless complexity.

Everything from this line and to the end of the loop should really be extracted into a separate function.

Getting rid of good_river() was a deliberate choice. I don't really trust compiler that it will inline it properly, and won't call it thousands of times, passing increased amount of arguments(overmaps refs), and then extracting x and y with getters, instead of looking at raw loop counter.
I most likely being too paranoid here, but mapgen is already uncomfortable slow, plus new checks of outer overmaps puts additional strain, which i wanted to lessen as much as reasonable possible. I'm almost sure that difference won't be noticeable by any measure, but don't see a reason to leave any chances to compiler to screw.
To my eye reducing indent won't improve readability - there's still no horizontal scroll, no forced line breaks, no astyle ranting, etc. It is wide, but not to the point where it causing discomfort. I'd say it's standing on the edge of the gray zone :) But it if it's important to you, then sure, i can split them back.

@olanti-p
Copy link
Contributor

There's a lot to debate when it comes to abstractions, code reuse, reliability, readability and performance, but I can agree that it is unlikely anyone will touch this code for years to come, and it's out of the way, so these topics don't matter much for this case or warrant holding up the PR over.

Copy link
Contributor

@olanti-p olanti-p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this bug, it's been around since forever.

@olanti-p olanti-p merged commit ed684bd into cataclysmbnteam:upload Sep 20, 2023
15 of 16 checks passed
@Vollch
Copy link
Contributor Author

Vollch commented Sep 20, 2023

It is indeed very debatable topic. I really don't feel good about deliberate degrading of performance without solid reason. In my world ugly code is better than slow code :) I might be unreasonable too strict about such things, so feel free to insist when you feel it does matter. I'm not stubbornly refusing - just wanted to explain my reasoning, and ask for confirmation - whether i really should do it. Anyway, glad it's settled down.

@Vollch Vollch deleted the rivers branch September 20, 2023 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
src changes related to source code.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants